package liming.tool.json;

import liming.tool.json.annotation.*;
import org.json.JSONArray;
import org.json.JSONObject;

import java.lang.reflect.Field;
import java.util.*;

import static liming.tool.json.Tool.*;

class JsonConstructor {
    public static JSONObject serialize(JsonSerializer jsonSerializer, Class<?> type) {
        JSONObject jsonObject = Serialize.serializeObject(jsonSerializer, type);
        if (type.isAnnotationPresent(JsonTime.class))
            jsonObject.put("$time", System.currentTimeMillis());
        return jsonObject;
    }

    private static class Serialize {
        private static Object serialize(Object o, Class<?> type) {
            if (o == null)
                return null;
            if (o instanceof JsonSerializer)
                return ((JsonSerializer) o).serializeBody(type);
            if (o instanceof Collection)
                return serializeCollection((Collection<?>) o, type);
            if (o instanceof Map)
                return serializeMap((Map<?, ?>) o, type);
            return o;
        }

        private static JSONObject serializeObject(JsonSerializer jsonSerializer, Class<?> type) {
            JSONObject jsonObject = new JSONObject();
            Field[] declaredFields;
            if (type.isInstance(jsonSerializer))
                declaredFields = type.getDeclaredFields();
            else
                declaredFields = jsonSerializer.getClass().getDeclaredFields();
            for (Field field : declaredFields)
                if (field.isAnnotationPresent(JsonMark.class))
                    serializeField(jsonObject, field, jsonSerializer);
            return jsonObject;
        }

        private static void serializeField(JSONObject root, Field field, Object obj) {
            JsonMark annotation = field.getAnnotation(JsonMark.class);
            if (!isPermission(annotation.permissions(), JsonPermission.Getter))
                return;
            Object value = getFieldValue(field, obj);
            if (value == null)
                return;
            root.put(getName(annotation, field.getName()), serialize(value, getClassType(field.getAnnotation(JsonType.class), field.getType())));
        }

        private static JSONArray serializeCollection(Collection<?> collection, Class<?> type) {
            JSONArray jsonArray = new JSONArray();
            if (collection == null)
                return jsonArray;
            for (Object o : collection)
                jsonArray.put(serialize(o, type));
            return jsonArray;
        }

        private static JSONObject serializeMap(Map<?, ?> map, Class<?> type) {
            JSONObject jsonObject = new JSONObject();
            if (map == null)
                return jsonObject;
            for (Map.Entry<?, ?> entry : map.entrySet())
                jsonObject.put(String.valueOf(entry.getKey()), serialize(entry.getValue(), type));
            return jsonObject;
        }
    }

    public static void deserialize(JsonDeserializer jsonDeserializer, JSONObject json) {
        Deserialize.deserializeObject(jsonDeserializer, json);
    }


    private static class Deserialize {
        // 类型化反序列化入口（增强版）
        private static Object deserialize(Class<?> needType, Object value, Field contextField) {
            if (value == null) {
                return ValueOf.getDefault(contextField.getType());
            }

            // 优先处理集合类型
            if (contextField != null) {
                if (contextField.isAnnotationPresent(JsonMarkCollection.class)) {
                    return handleCollectionType(needType,ValueOf.valueOf(JSONArray.class, value), contextField);
                }
                if (contextField.isAnnotationPresent(JsonMarkMap.class)) {
                    return handleMapType(needType, ValueOf.valueOf(JSONObject.class, value), contextField);
                }
            }

            // 基础类型处理
            if (needType.isPrimitive() || Tool.isWrapperType(needType)) {
                return ValueOf.valueOf(needType, value);
            }

            // 递归处理复杂类型
            if (value instanceof JSONObject|| value instanceof Map) {
                return handleJsonObject(needType, ValueOf.valueOf(JSONObject.class, value));
            }
            return ValueOf.valueOf(needType, value);
        }

        private static <T extends JsonDeserializer> void deserializeObject(JsonDeserializer jsonDeserializer, JSONObject data) {
            Field[] fields = jsonDeserializer.getDefaultClass().getDeclaredFields();
            for (Field field : fields)
                if (field.isAnnotationPresent(JsonMark.class))
                    deserializeField(field, jsonDeserializer, data);
        }

        // 处理JSON对象（增强类型支持）
        private static Object handleJsonObject(Class<?> type, JSONObject json) {
            try {
                if (JsonDeserializer.class.isAssignableFrom(type)) {
                    JsonDeserializer instance = (JsonDeserializer) type.getDeclaredConstructor().newInstance();
                    deserializeObject(instance, json);
                    return instance;
                }
                return ValueOf.valueOf(type, json);
            } catch (Exception e) {
                return ValueOf.getDefault(type);
            }
        }

        // 集合类型处理（支持注解配置）
        private static Collection<?> handleCollectionType(Class<?> containerType, JSONArray jsonArray, Field field) {
            JsonMarkCollection annotation = field.getAnnotation(JsonMarkCollection.class);
            Class<?> elementType = annotation.value();

            Collection<Object> collection = createCollectionInstance(containerType);
            for (Object item : jsonArray) {
                collection.add(deserialize(elementType, item, null)); // 元素类型已知
            }
            return collection;
        }

        // Map类型处理（支持键值类型配置）
        private static Map<?, ?> handleMapType(Class<?> mapType, JSONObject jsonObject, Field field) {
            JsonMarkMap annotation = field.getAnnotation(JsonMarkMap.class);
            Class<?> keyType = annotation.key();
            Class<?> valueType = annotation.value();

            Map<Object, Object> map = createMapInstance(mapType);
            jsonObject.toMap().forEach((key,value)->{
                map.put(
                        deserialize(keyType, key, null), // 键类型转换
                        deserialize(valueType, value, null) // 值类型转换
                );
            });
            return map;
        }

        // 字段反序列化（精确类型处理）
        private static void deserializeField(Field field, Object obj, JSONObject json) {
            JsonMark jsonMark = field.getAnnotation(JsonMark.class);
            if (!Tool.isPermission(jsonMark.permissions(), JsonPermission.Setter)) return;

            String jsonKey = Tool.getName(jsonMark, field.getName());
            if (!json.has(jsonKey)&&!Tool.isPermission(jsonMark.permissions(), JsonPermission.AutoValue)) {
                return;
            }
            Class<?> fieldType = getActualType(field);
            Object value = deserialize(fieldType, json.opt(jsonKey), field);

            if(value!=null)
                setFieldValue(field, obj, value);
        }

        // 获取字段实际类型（支持JsonType覆盖）
        private static Class<?> getActualType(Field field) {
            JsonType jsonType = field.getAnnotation(JsonType.class);
            return jsonType != null ? jsonType.value() : field.getType();
        }

        // 安全集合实例创建
        private static Collection<Object> createCollectionInstance(Class<?> collectionType) {
            try {
                return (Collection<Object>) collectionType.getDeclaredConstructor().newInstance();
            } catch (Exception e) {
                if (List.class.isAssignableFrom(collectionType)) return new ArrayList<>();
                if (Set.class.isAssignableFrom(collectionType)) return new HashSet<>();
                return Collections.singleton(ValueOf.getDefault(collectionType));
            }
        }

        // 安全Map实例创建
        private static Map<Object, Object> createMapInstance(Class<?> mapType) {
            try {
                return (Map<Object, Object>) mapType.getDeclaredConstructor().newInstance();
            } catch (Exception e) {
                return new LinkedHashMap<>();
            }
        }
    }

}
